/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#include "NetVars.hpp"

#include <boost/lexical_cast.hpp>

#include "gen/netmsg.pb.h"
#include "Net.hpp"

namespace spelprog2 {
namespace net {

NetVars::NetVars(bool isServer) :
    users(),
    nullstr(""),
    nextId(FIRST_FREE_ID),
    net_(),
    isServer_(isServer)
{
    users[SERVER_ID] = UserPtr(new User());
}
   
NetVars::~NetVars()
{
}

UserId NetVars::addUser()
{
    assert(net_);
    assert(isServer_);

    UserId id = nextId++;
    netlog << "Adding user #" + boost::lexical_cast<std::string>(id);
    users[id] = UserPtr(new User());

    netmsg::UserChange userChange;
    userChange.set_action(spelprog2::netmsg::ADD_USER);
    userChange.set_userid(id);
    net_->sendToAll(userChange);

    return id;
}

void NetVars::addUser(UserId id)
{
    if (id >= nextId) {
        nextId = id + 1;
    }

    netlog << "Adding user #" + boost::lexical_cast<std::string>(id);
    users[id] = UserPtr(new User());
}

void NetVars::refreshToUser(UserId user)
{
    assert(net_);
    assert(isServer_);

    //TODO: verify overhead of this method is OK for large var sets
    netmsg::ServerVarChange varChange;
    for (UserMap::const_iterator it = users.begin(), end = users.end(); it != end; ++it) {
        varChange.set_userid(it->first);

        for (Vars::const_iterator jt = it->second->begin(), end2 = it->second->end(); jt != end2; ++jt) {
            varChange.set_key(jt->first);
            varChange.set_value(jt->second);
            net_->sendToUser(varChange, user);
        }
    }
}

void NetVars::removeUser(UserId id)
{
    assert(net_);

    UserMap::iterator it = users.find(id);
    if (it != users.end()) {
        netlog << "Removing user #" + boost::lexical_cast<std::string>(id);

        users.erase(it);

        if (isServer_) {
            netmsg::UserChange userChange;
            userChange.set_action(spelprog2::netmsg::DELETE_USER);
            userChange.set_userid(id);
            net_->sendToAll(userChange);
        }
    }
}

bool NetVars::updateVar(UserId user, const std::string & key, const std::string & val)
{
    UserMap::iterator it = users.find(user);
    if (it == users.end()) {
        return false;
    }

    if (val == "") {
        Vars::iterator valIt = it->second->find(key);
        if (valIt != it->second->end()) {
            it->second->erase(valIt);
        }
    }
    else {
        (*it->second)[key] = val;
    }

    netlog << "Made var change (" + boost::lexical_cast<std::string>(user) + ", " + key + ", " + val + ")";

    return true;
}

void NetVars::setVar(UserId user, const std::string & key, const std::string & val)
{
    assert(net_);

    netmsg::ServerVarChange varChange;
    varChange.set_userid(user);
    varChange.set_key(key);
    varChange.set_value(val);

    if (isServer_) {
        if (updateVar(user, key, val)) {
            net_->sendToAll(varChange);
        }
    }
    else {
        net_->sendToUser(varChange, SERVER_ID);
        netlog << "Sent request to make var change (" + boost::lexical_cast<std::string>(user) + ", " + key + ", " + val + ")";
    }
}

const std::string & NetVars::getVar(UserId id, const std::string & key) const
{
    UserMap::const_iterator it = users.find(id);
    if (it == users.end()) {
        return nullstr;
    }

    VarsConstIterator varIt = it->second->find(key);
    if (varIt == it->second->end()) {
        return nullstr;
    }
    
    return varIt->second;
}

NetVars::VarsConstIterator NetVars::getVars(UserId id) const
{
    UserMap::const_iterator it = users.find(id);
    if (it == users.end()) {
        return users.find(SERVER_ID)->second->end();
    }

    return it->second->begin();
}

void NetVars::dump()
{
    gamelog << "Dumping NetVars:";
    for (UserMap::const_iterator it = users.begin(), end = users.end(); it != end; ++it) {
        gamelog << "User " + STRING_CAST(it->first) + " has the following vars:";

        for (Vars::const_iterator jt = it->second->begin(), end2 = it->second->end(); jt != end2; ++jt) {
            gamelog << "\t" + jt->first + " = \"" + jt->second + "\"";
        }
    }
}

}; //namespace net
}; //namespace spelprog2
